package com.linciping.gradle.inject.view.utils

import org.objectweb.asm.Opcodes

object ClickMethodUtils {
    const val CLICK_LISTENER_NAME = "android/view/View/OnClickListener"
    const val CLICK_METHOD_NAME = "onClick"
    const val VIEW_TYPE = "Landroid/view/View"
    const val CLICK_METHOD_DESC = "(Landroid/view/View;)V"
    const val VOID_RETURN = "V"

    /**
     * 判断是否时onClick事件的回调方法，条件如下：
     * 1. 如果是构造方法直接返回false
     * 2. 如果是一个标准的View.OnClickListener子类，并且有onClick(Landroid/view/View;)V方法，则直接返回true
     * 3. 如果是动态方法模式，查找看方法参数是否有Landroid/view/View，并且返回是否为空，如果不符合，直接返回false
     * 4. 如果包含参数和返回类型，判断方法名是否包含当前类的方法名和lambda标记，如果存在则返回true，否则返回false
     * @param methodNameList 当前已存在的方法
     * @param name 当前处理的方法名称
     * @param descriptor 当前处理的方法描述
     * @param superClassName 当前类的父类
     */
    fun isViewClickMethod(
        methodNameList: List<String>,
        name: String,
        descriptor: String,
        superClassName: String
    ): Boolean {
        if ("<init>" == name) {
            return false
        }
        if (superClassName == CLICK_LISTENER_NAME &&
            name == CLICK_METHOD_NAME &&
            descriptor == CLICK_METHOD_DESC
        ) {
            return true
        }
        if (!isAllowClickMethodDesc(descriptor)) {
            return false
        }
        val names = name.split("\$")
        val lastMethodName = names.last()
        val parentMethodName = name.replace("$$lastMethodName", "")
        return methodNameList.contains(parentMethodName)
                && lastMethodName.contains("lambda")
    }

    /**
     * 是否是一个符合onClick回到方法的描述
     * @param descriptor 当前处理的方法描述
     */
    private fun isAllowClickMethodDesc(descriptor: String): Boolean {
        if (descriptor == CLICK_METHOD_DESC) return true
        val descs = descriptor.split(")")
        val returnType = descs[1]
        if (returnType != VOID_RETURN) return false
        val descParam = descs[0].replace("(", "")
        val params = descParam.split(";")
        for (param in params) {
            if (param == VIEW_TYPE) {
                return true
            }
        }
        return false
    }

    /**
     * 获取view参数的位置
     * 如果是非静态方法，会后移一位
     */
    fun getViewParamIndex(descriptor: String, access: Int): Int {
        val isStatic = (access and Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC
        val descs = descriptor.split(")")
        val descParam = descs[0].replace("(", "")
        val params = descParam.split(";")
        val index = params.indexOf(VIEW_TYPE)
        return if (isStatic) {
            index
        } else {
            index + 1
        }
    }
}